﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.IO;
using System.Xml;
using System.Xml.Serialization;



namespace Sublimation
{
    public class EnemyUnit : Unit
    {
        protected int NUMBER_OF_FRAMES_TO_PLAY = 6;
        protected int FRAMES_PER_SECOND = 10;

        // the animation
        protected AnimatedSprite.AnimatedTexture textureAnimator;
        protected AnimatedSprite.AnimatedTexture textureAttack;

        protected const float EnemyUnitGoalBehaviourWeight = 1.0f;

        protected int scoreValue;
        protected EnemyKind kind;
        public EnemyKind Kind
        {
            get { return kind; }
        }

        public virtual void setProperties()
        {
            XmlSerializer s = new XmlSerializer(typeof(EnemyProperties));
            EnemyProperties enemyP;
            TextReader r = null;

            switch (kind)
            {
                case EnemyKind.BigCreep:
                r = new StreamReader("Resources/XML/enemies/big_creep.xml");
                break;
                case EnemyKind.BigBoss:
                r = new StreamReader("Resources/XML/enemies/big_boss.xml");
                break;
                case EnemyKind.Boss:
                r = new StreamReader("Resources/XML/enemies/boss.xml");
                break;
                case EnemyKind.WhipGall:
                r = new StreamReader("Resources/XML/enemies/whip_gall.xml");
                break;
                case EnemyKind.Kamikaze:
                r = new StreamReader("Resources/XML/enemies/kamikaze.xml");
                break;
                case EnemyKind.SmallCreep:
                r = new StreamReader("Resources/XML/enemies/small_creep.xml");
                break;
                case EnemyKind.Priest:
                r = new StreamReader("Resources/XML/enemies/priest.xml");
                break;
                case EnemyKind.Wizard:
                r = new StreamReader("Resources/XML/enemies/wizard.xml");
                break;
            }
            enemyP = (EnemyProperties)s.Deserialize(r);

            CollisionRadius = enemyP.CollisionRadius;
            MaxLife = enemyP.MaxLife;
            MaxSpeed = enemyP.MaxSpeed;
            maxTurnRadiansPerSec = enemyP.MaxTurnRadiansPerSec;
            meleeDamage = enemyP.MeleeDamage;
            meleeAttackDelay = enemyP.MeleeAttackDelay;
            scoreValue = enemyP.ScoreValue;
        }

        public EnemyUnit(WorldProperties worldProperties, EnemyKind kind, int spawnPoint)
            : base(worldProperties)
        {
            LoadContent();
            this.kind = kind;
            setProperties();
            Life = MaxLife;
            worldProperties.EnemyCount++;

            // make sure it loads and draws
            DrawOrder = 50;
            UpdateOrder = 50;
            Visible = true;
            Enabled = true;

            SetOriginToCentre();

            SetInitialPosition(spawnPoint);

            //EEE testing
            //position = new Vector2(500, 500);

            // give the unit a direction
            velocity = Vector2.Zero;
            //XXX:for now just head for the centre of the map (roughly)
            velocity = new Vector2(
                worldProperties.Map.WorldBounds.Width / 2 + worldProperties.Map.WorldBounds.X + worldProperties.Random.Next(0, 50),
                worldProperties.Map.WorldBounds.Height / 2 + worldProperties.Map.WorldBounds.Y + worldProperties.Random.Next(0, 50))
                - Position;
            velocity.Normalize();
            velocity *= MaxSpeed;
        }

        public EnemyUnit(WorldProperties worldProperties,EnemyKind kind)
            : this(worldProperties, kind, -1)
        {
        }

        protected virtual void SetInitialPosition(int spawnPoint)
        {
            // place the unit just outside the map somewhere
            //XXX:this could update to include map spawning points etc, maybe some units jump into the play field
            /*int perimeterPosition = worldProperties.Random.Next(0, worldProperties.Map.Perimeter);
            if (perimeterPosition <= worldProperties.Map.WorldBounds.Width)
            {
                // place on the top
                Position = new Vector2(worldProperties.Map.WorldBounds.X + perimeterPosition, worldProperties.Map.WorldBounds.Y + 1);
            }
            else if (perimeterPosition <= worldProperties.Map.WorldBounds.Width + worldProperties.Map.WorldBounds.Height)
            {
                // right side
                perimeterPosition -= worldProperties.Map.WorldBounds.Width;
                Position = new Vector2(worldProperties.Map.WorldBounds.X + worldProperties.Map.WorldBounds.Width - 1, worldProperties.Map.WorldBounds.Y + perimeterPosition);
            }
            else if (perimeterPosition <= worldProperties.Map.WorldBounds.Width * 2 + worldProperties.Map.WorldBounds.Height)
            {
                // bottom side
                perimeterPosition -= worldProperties.Map.WorldBounds.Width + worldProperties.Map.WorldBounds.Height;
                Position = new Vector2(worldProperties.Map.WorldBounds.X + perimeterPosition, worldProperties.Map.WorldBounds.Y + worldProperties.Map.WorldBounds.Height - 1);
            }
            else
            {
                // left side
                perimeterPosition -= worldProperties.Map.WorldBounds.Width * 2 + worldProperties.Map.WorldBounds.Height;
                Position = new Vector2(worldProperties.Map.WorldBounds.X + 1, worldProperties.Map.WorldBounds.Y + perimeterPosition);
            }*/

            // spawn at a spawnpoint
            int spawnChoice;
            if (spawnPoint >= 0) spawnChoice = spawnPoint;
            else spawnChoice = worldProperties.Random.Next(WorldProperties.MAX_SPAWN_POINTS);
            Position = worldProperties.spawnPoints[spawnChoice].Position
                + (float)worldProperties.Random.NextDouble() * SpawnPoint.MAX_ENEMY_SPAWN_DISTANCE * new Vector2((float)Math.Cos(worldProperties.Random.NextDouble() * MathHelper.TwoPi), (float)Math.Sin(worldProperties.Random.NextDouble() * MathHelper.TwoPi));

            // set the rotation of the unit to face towards the center
            Rotation = worldProperties.spawnPoints[spawnChoice].RotationToCenter;
        }

        //TODO: Update this to use a texture strip and the animated texture stuff.
        protected bool left = true;
        protected const int MOVE_FRAMES = 9;
        protected int lastFrames = 0;
        public override void Update(GameTime gameTime)
        {
            attacking = false;
            base.Update(gameTime);

            // update the animation
            if (attacking && textureAttack!= null)
            {
                textureAttack.UpdateFrame((float)gameTime.ElapsedGameTime.TotalSeconds);
            }
            else
            {
                textureAnimator.UpdateFrame((float)gameTime.ElapsedGameTime.TotalSeconds);
            }
        }

        protected override void LoadContent()
        {
            base.LoadContent();

            CollisionRadius = 16f;
        }

        public override void Draw(GameTime gameTime)
        {
            base.Draw(gameTime);

            if (attacking && textureAttack != null)
            {
                textureAttack.Rotation = Rotation - MathHelper.PiOver2;
                spriteBatch.Begin();
                textureAttack.DrawFrame(spriteBatch, Position);
                spriteBatch.End();
            }
            else
            {
                textureAnimator.Rotation = Rotation - MathHelper.PiOver2;
                spriteBatch.Begin();
                textureAnimator.DrawFrame(spriteBatch, Position);
                spriteBatch.End();
            }
        }

        public override void Kill()
        {
                base.Kill();

                // did we die from loss of life?
                if (Life <= 0)
                {
                    // spawn an explosion if we died from loss of life
                    //new Animation(worldProperties, this,collisionRadius*.04f, 5, 10, "Sprites\\bullet_hit_enemy");
                    // increase player score
                    worldProperties.HighScore.CurrentScore += scoreValue;
                    worldProperties.HighScore.enemyKilled(kind);
                    worldProperties.DrawTextEnemyDamaged(scoreValue, Position);
                }
                // reduce enemy count

                worldProperties.EnemyCount--;
            worldProperties.Trash.RemovableEnemies.Add(this);
        }

        protected override void collideWithTower(Tower tower, GameTime gameTime)
        {
            base.collideWithTower(tower, gameTime);

            // damage tower
            MeleeAttack(tower);
        }

        protected override void SetupBehaviours()
        {
            base.SetupBehaviours();

            this.behaviours.Add(new Behaviours.EnemyUnitGoalBehaviour(worldProperties, this, EnemyUnitGoalBehaviourWeight));

            AISightRange = 300.0f;
        }

        public override void SetOriginToCentre()
        {
            this.Origin = new Vector2(collisionRadius, collisionRadius);
        } 

        public override void UpdateBoundingRectangle()
        {
            
            // position is at the origin, so need to take that into account otherwise the bounding rectangle will be in a strange position
            boundingRectangle = new Rectangle((int)(Position.X) - (int)CollisionRadius, (int)(Position.Y) - (int)CollisionRadius, (int)CollisionRadius*2, (int)CollisionRadius*2);
        }

        public override void ApplyDamage(float damage)
        {
            base.ApplyDamage(damage);
            
            // also display some text to the screen when damage is done
            //worldProperties.DrawTextEnemyDamaged(damage, Position);
        }
    }
}
